<script lang="ts">
	import { Alert, Button, Tab, Tabs } from '$lib/components/common'
	import ScriptPicker from '$lib/components/ScriptPicker.svelte'
	import Toggle from '$lib/components/Toggle.svelte'
	import type { Schema, SupportedLanguage } from '$lib/common'
	import { base } from '$lib/base'
	import { enterpriseLicense, workspaceStore } from '$lib/stores'
	import { emptySchema, emptyString, sendUserToast, tryEvery } from '$lib/utils'
	import {
		FlowService,
		JobService,
		type Script,
		ScriptService,
		WorkspaceService,
		type Flow
	} from '$lib/gen'
	import { inferArgs } from '$lib/infer'
	import { hubBaseUrlStore } from '$lib/stores'

	import { CheckCircle2, Loader2, RotateCw, XCircle } from 'lucide-svelte'
	import { hubPaths } from '$lib/hub'

	const slackRecoveryHandler = hubPaths.slackRecoveryHandler
	const slackHandlerScriptPath = hubPaths.slackErrorHandler
	const slackSuccessHandler = hubPaths.slackSuccessHandler

	export let errorOrRecovery: 'error' | 'recovery' | 'success'
	export let isEditable: boolean
	export let slackToggleText: string = 'Enable'
	export let showScriptHelpText: boolean = false
	export let handlerSelected: 'custom' | 'slack'

	export let handlerPath: string | undefined
	export let handlerExtraArgs: Record<string, any>

	export let customInitialScriptPath: string | undefined
	export let customScriptTemplate: string
	export let customHandlerKind: 'flow' | 'script' = 'script'
	let customHandlerSchema: Schema | undefined

	let slackHandlerSchema: Schema | undefined
	let workspaceConnectedToSlack: boolean | undefined = undefined
	let slackConnectionTestJob:
		| { uuid: string; is_success: boolean; in_progress: boolean }
		| undefined

	async function loadSlackResources() {
		const settings = await WorkspaceService.getSettings({ workspace: $workspaceStore! })
		if (!emptyString(settings.slack_name) && !emptyString(settings.slack_team_id)) {
			workspaceConnectedToSlack = true
		} else {
			workspaceConnectedToSlack = false
		}
	}

	async function sendSlackMessage(channel: string): Promise<void> {
		let submitted_job = await WorkspaceService.runSlackMessageTestJob({
			workspace: $workspaceStore!,
			requestBody: {
				hub_script_path: handlerPath,
				channel: channel,
				test_msg: `This is a notification to test the connection between Slack and Windmill workspace '${$workspaceStore!}'`
			}
		})
		slackConnectionTestJob = {
			uuid: submitted_job.job_uuid!,
			in_progress: true,
			is_success: false
		}
		tryEvery({
			tryCode: async () => {
				const testResult = await JobService.getCompletedJob({
					workspace: $workspaceStore!,
					id: slackConnectionTestJob!.uuid
				})
				slackConnectionTestJob!.in_progress = false
				slackConnectionTestJob!.is_success = testResult.success
			},
			timeoutCode: async () => {
				try {
					await JobService.cancelQueuedJob({
						workspace: $workspaceStore!,
						id: slackConnectionTestJob!.uuid,
						requestBody: {
							reason: 'Slack message not sent after 5s'
						}
					})
				} catch (err) {
					console.error(err)
				}
			},
			interval: 500,
			timeout: 5000
		})
	}

	async function loadHandlerScriptArgs(p: string, defaultArgs: string[] = []) {
		try {
			let schema: Schema | undefined = emptySchema()
			if (p.startsWith('hub/')) {
				const hubScript = await ScriptService.getHubScriptByPath({
					path: p
				})

				if ((hubScript.schema as any)?.properties) {
					schema = hubScript.schema as any
				} else {
					await inferArgs(hubScript.language as SupportedLanguage, hubScript.content ?? '', schema)
				}
			} else {
				let scriptOrFlow: Script | Flow =
					customHandlerKind === 'script'
						? await ScriptService.getScriptByPath({ workspace: $workspaceStore!, path: p })
						: await FlowService.getFlowByPath({ workspace: $workspaceStore!, path: p })
				schema = scriptOrFlow.schema as Schema
			}
			if (schema && schema.properties) {
				for (let key in schema.properties) {
					if (defaultArgs.includes(key)) {
						delete schema.properties[key]
					}
				}
				return schema
			}
		} catch (err) {
			sendUserToast(`Could not query handler schema: ${err}`, true)
		}
	}

	function isSlackHandler(scriptPath: string | undefined) {
		if (scriptPath === undefined) {
			return false
		}
		if (errorOrRecovery == 'error') {
			return (
				scriptPath.startsWith('hub/') &&
				scriptPath.endsWith('/workspace-or-schedule-error-handler-slack')
			)
		} else if (errorOrRecovery == 'recovery') {
			return (
				scriptPath.startsWith('hub/') && scriptPath.endsWith('/schedule-recovery-handler-slack')
			)
		} else {
			return scriptPath.startsWith('hub/') && scriptPath.endsWith('/schedule-success-handler-slack')
		}
	}

	$: {
		if ($workspaceStore) {
			loadSlackResources()
		}
	}

	$: handlerPath &&
		!isSlackHandler(handlerPath) &&
		loadHandlerScriptArgs(handlerPath, [
			'path',
			'workspace_id',
			'job_id',
			'is_flow',
			'schedule_path',
			'error',
			'error_started_at',
			'failed_times',
			'started_at',
			'success_times',
			'success_result',
			'success_started_at',
			'email'
		]).then((schema) => (customHandlerSchema = schema))

	$: handlerPath &&
		isSlackHandler(handlerPath) &&
		loadHandlerScriptArgs(handlerPath, [
			'path',
			'workspace_id',
			'job_id',
			'is_flow',
			'schedule_path',
			'error',
			'error_started_at',
			'failed_times',
			'started_at',
			'success_times',
			'success_result',
			'success_started_at',
			'email',
			'slack'
		]).then((schema) => (slackHandlerSchema = schema))
</script>

<div>
	<Tabs bind:selected={handlerSelected} class="mt-2 mb-4">
		<Tab value="slack">Slack</Tab>
		<Tab value="custom">
			Custom
			<slot name="custom-tab-tooltip" />
		</Tab>
	</Tabs>
</div>

{#if handlerSelected === 'custom'}
	<div class="flex flex-row mb-2">
		<ScriptPicker
			disabled={!isEditable || !$enterpriseLicense}
			initialPath={customInitialScriptPath}
			kinds={['script', 'failure']}
			allowFlow={true}
			bind:scriptPath={handlerPath}
			bind:itemKind={customHandlerKind}
			allowRefresh
		/>

		{#if handlerPath === undefined}
			<Button
				btnClasses="ml-4 mt-2"
				color="dark"
				size="xs"
				href={customScriptTemplate}
				target="_blank">Create from template</Button
			>
		{/if}
	</div>
	{#if showScriptHelpText}
		<div class="text-xs">
			Example of error handler scripts can be found on <a
				target="_blank"
				href="{$hubBaseUrlStore}/failures"
			>
				Windmill Hub</a
			>
		</div>
	{/if}
	{#if handlerPath}
		<p class="font-semibold text-sm mt-4 mb-2">Extra arguments</p>
		{#await import('$lib/components/SchemaForm.svelte')}
			<Loader2 class="animate-spin" />
		{:then Module}
			<Module.default
				disabled={!isEditable}
				schema={customHandlerSchema}
				bind:args={handlerExtraArgs}
				shouldHideNoInputs
				class="text-xs"
			/>
		{/await}
		{#if customHandlerSchema && customHandlerSchema.properties && Object.keys(customHandlerSchema.properties).length === 0}
			<div class="text-xs texg-gray-700">This error handler takes no extra arguments</div>
		{/if}
	{/if}
{:else if handlerSelected === 'slack'}
	<span class="w-full flex mb-3">
		<Toggle
			disabled={!$enterpriseLicense || !isEditable}
			checked={isSlackHandler(handlerPath)}
			options={{ right: slackToggleText }}
			on:change={async (e) => {
				if (e.detail && errorOrRecovery === 'error') {
					handlerPath = slackHandlerScriptPath
				} else if (e.detail && errorOrRecovery === 'recovery') {
					handlerPath = slackRecoveryHandler
				} else if (e.detail && errorOrRecovery === 'success') {
					handlerPath = slackSuccessHandler
				} else {
					handlerPath = undefined
				}
			}}
		/>
	</span>
	{#if workspaceConnectedToSlack}
		{#await import('$lib/components/SchemaForm.svelte')}
			<Loader2 class="animate-spin" />
		{:then Module}
			<Module.default
				disabled={!$enterpriseLicense || !isSlackHandler(handlerPath)}
				schema={slackHandlerSchema}
				schemaSkippedValues={['slack']}
				schemaFieldTooltip={{
					channel: 'Slack channel name without the "#" - example: "windmill-alerts"'
				}}
				bind:args={handlerExtraArgs}
				shouldHideNoInputs
				class="text-xs"
			/>
		{/await}
	{:else if workspaceConnectedToSlack == undefined}
		<Loader2 class="animate-spin" size={10} />
	{/if}
	{#if $enterpriseLicense && isSlackHandler(handlerPath)}
		{#if workspaceConnectedToSlack == false}
			<Alert type="error" title="Workspace not connected to Slack">
				<div class="flex flex-row gap-x-1 w-full items-center">
					<p class="text-clip grow min-w-0">
						The workspace needs to be connected to Slack to use this feature. You can <a
							target="_blank"
							href="{base}/workspace_settings?tab=slack">configure it here</a
						>.
					</p>
					<Button
						variant="border"
						color="light"
						on:click={loadSlackResources}
						startIcon={{ icon: RotateCw }}
					/>
				</div>
			</Alert>
		{:else}
			<Button
				disabled={emptyString(handlerExtraArgs['channel'])}
				btnClasses="w-32 text-center"
				color="dark"
				on:click={() => sendSlackMessage(handlerExtraArgs['channel'])}
				size="xs">Send test message</Button
			>
			{#if slackConnectionTestJob !== undefined}
				<p class="text-normal text-2xs mt-1 gap-2">
					{#if slackConnectionTestJob.in_progress}
						<RotateCw size={14} />
					{:else if slackConnectionTestJob.is_success}
						<CheckCircle2 size={14} class="text-green-600" />
					{:else}
						<XCircle size={14} class="text-red-700" />
					{/if}
					Message sent via Windmill job
					<a
						target="_blank"
						href={`${base}/run/${slackConnectionTestJob.uuid}?workspace=${$workspaceStore}`}
					>
						{slackConnectionTestJob.uuid}
					</a>
				</p>
			{/if}
		{/if}
	{/if}
{/if}
